package com.shaoyu.tools.event.core;


import com.google.common.base.Preconditions;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.shaoyu.tools.event.annotation.EventInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;

import java.lang.reflect.Method;


/**
 * @Desc :
 * @Author: shaoyu
 * @Date : 2022/11/1 10:53
 */
public class EventBusExecutor {

    private static EventBus INSTANCE;

    private static void init(EventBus eventBus){
        INSTANCE = eventBus;
        register(SimpleEventObserver.INSTANCE);
    }

    public static void register(Object observer) {
        Preconditions.checkNotNull(observer);
        INSTANCE.register(observer);
    }

    public static void sendEvent(Object event) {
        Preconditions.checkNotNull(event);
        INSTANCE.post(event);
    }

    public static void unregister(Object observer) {
        Preconditions.checkNotNull(observer);
        INSTANCE.unregister(observer);
    }

    public static void run(ConcurrentEventRunnable eventRunnable){
        Preconditions.checkNotNull(eventRunnable);
        sendEvent(eventRunnable);
    }

    public static void runByQueue(QueueEventRunnable eventRunnable){
        Preconditions.checkNotNull(eventRunnable);
        sendEvent(eventRunnable);
    }

    private enum SimpleEventObserver {
        INSTANCE;
        @Subscribe
        @AllowConcurrentEvents
        public void call(ConcurrentEventRunnable eventRunnable) throws Throwable{
            eventRunnable.run();
        }
        @Subscribe
        public void call(QueueEventRunnable eventRunnable) throws Throwable{
            eventRunnable.run();
        }
    }

    public static class EventBusApplicationListener implements ApplicationListener<ApplicationStartedEvent> {

        private Logger logger = LoggerFactory.getLogger(EventBusApplicationListener.class);

        @Override
        public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
            ConfigurableApplicationContext applicationContext = applicationStartedEvent.getApplicationContext();
            EventBus eventBus = applicationContext.getBean(EventBus.class);
            EventBusExecutor.init(eventBus);
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            logger.info("loading Subscribe and Interceptor...");
            beans:
            for (String beanDefinitionName : beanDefinitionNames) {
                Object bean = applicationContext.getBean(beanDefinitionName);
                Class<?> aClass = bean.getClass();
                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    EventInterceptor interceptorAnnotation = method.getAnnotation(EventInterceptor.class);
                    if (subscribeAnnotation != null || interceptorAnnotation != null) {
                        EventBusExecutor.register(bean);
                        continue beans;
                    }
                }
            }
            logger.info("load Subscribe and Interceptor Success!");
        }
    }

}
